<template>
  <!--#region 顶部工具栏-->
  <div class="flex items-center p-2" style="--el-fill-color-light: #eaebec">
    <el-button ref="fieldButtonRef" text @click="fieldManagementRef?.toggleVisible(true)">
      <i class="i-lucide-columns-3" />
      <span class="inline-block ml-2">字段管理</span>
    </el-button>
    <el-divider class="h-[20px]" direction="vertical" />
    <el-button ref="filterButtonRef" :bg="hasFilter" text @click="onSettingUpFilter($event)">
      <i class="i-lucide-filter" />
      <span class="inline-block ml-2">筛选</span>
    </el-button>
    <el-button ref="groupingConfigButtonRef" :bg="hasGrouping" text @click="onSettingUpGrouping($event)">
      <i class="i-lucide-server" />
      <span class="inline-block ml-2">分组</span>
    </el-button>
    <el-button ref="sortingConfigButtonRef" :bg="hasSorting" text @click="onSettingUpSorting($event)">
      <i class="i-lucide-chevrons-up-down" />
      <span class="inline-block ml-2">排序</span>
    </el-button>
  </div>
  <!--#endregion-->
  <!--#region 表格容器-->
  <div ref="tabulatorWrapperRef" class="px-4 py-2" style="height: calc(100% - 44px)">
    <div id="tabulator"></div>
  </div>
  <!--#endregion-->
  <FieldManagement ref="fieldManagementRef" v-model="tabulatorColumns" :tabulator="tabulator" :view-id="viewId"
    :virtual-ref="fieldButtonRef" @update:definition="onUpdateColumnDefinition" />
  <FieldPicker ref="fieldPickerRef" />
  <FilterConfigPane ref="filterConfigRef" v-model="filterConfig" :col-enum-map="colEnumMap" :columns="columns"
    :field-picker="fieldPickerRef" :symbol-picker="symbolPickerRef" :virtual-ref="filterButtonRef" />
  <CompareSymbolPicker ref="symbolPickerRef" clear-model-value-when-hide />
  <GroupingConfigPane ref="groupingConfigRef" v-model="groupingConfig" :columns="columns" :field-picker="fieldPickerRef"
    :virtual-ref="groupingConfigButtonRef" @update:sort="setTableGroupingAndSorting"
    @update:group-by="onUpdateGroupBy" />
  <SortingConfigPane ref="sortingConfigRef" v-model="sortingConfig" :columns="selectableSortingColumns"
    :field-picker="fieldPickerRef" :virtual-ref="sortingConfigButtonRef" @update:sort-by="setTableSorting" />
</template>
<script lang="ts" setup>
import './style/tailwind-fix.css'
import './style/tabulator.css'
import './style/tailwind.css'
import 'tabulator-tables/dist/css/tabulator_bootstrap5.min.css'
import type {
  ColumnDefinition,
  ColumnLayout,
  Formatter,
  OptionsGeneral,
} from 'tabulator-tables'

import { TabulatorFull as Tabulator } from 'tabulator-tables'

import { nextTick, onMounted, onUnmounted, ref, shallowRef, defineAsyncComponent, computed, watch } from 'vue'
import type {
  ColumnConfig,
  DataProcessConfig,
  FilterConfig,
  GroupingConfig,
  SortingConfig
} from './types'
import {
  CompareSymbol,
  DateConditionValue,
  debounce,
  LogicSymbol,
  omit,
  select,
  TagColorList,
  template,
  withDefaultUnit,
  typeIcon,
  tryit,
  deepClone
} from './lib'
import { dayjs } from 'element-plus';

const FieldManagement = defineAsyncComponent(
  () => import('./components/FieldManagement.vue')
)
const FieldPicker = defineAsyncComponent(
  () => import('./components/FieldPicker.vue')
)
const FilterConfigPane = defineAsyncComponent(
  () => import('./components/FilterConfig.vue')
)
const CompareSymbolPicker = defineAsyncComponent(
  () => import('./components/CompareSymbolPicker.vue')
)

const GroupingConfigPane = defineAsyncComponent(
  () => import('./components/GroupingConfig.vue')
)

const SortingConfigPane = defineAsyncComponent(
  () => import('./components/SortingConfig.vue')
)

const props = defineProps<{
  data: any[]
  columns: ColumnConfig[]
  viewId: string
}>()

const emit = defineEmits<{
  init: [],
  'before-destroy': []
}>()

const tabulatorWrapperRef = ref()

// 表格组件实例
const tabulator = shallowRef<Tabulator | null>(null)

// 表格列配置
const tabulatorColumns = ref<ColumnDefinition[]>([])

// 表格布局配置
const tableLayout = defineModel<ColumnLayout[]>('layout', { required: false, default: [] })
//
const tableDataProcessConfig = defineModel<DataProcessConfig>('config', {
  required: false,
  default: {
    filter: {},
    sorter: [],
    grouping: []
  }
})


// 表格行头配置
const tabulatorRowHeader = ref<OptionsGeneral['rowHeader']>({})

const colEnumMap: Ref<Record<string, (string | number)[]>> = ref({})

// 枚举列单元格内容格式
const tagCellFormatter: Formatter = (cell, formatterParams) => {
  const { column } = formatterParams as { column: ColumnConfig }
  const value = cell.getValue()
  const index = column.enums?.findIndex(e => e === value) ?? -1
  let tagColor
  if (index === -1) tagColor = TagColorList.at(-1)
  else tagColor = TagColorList[index % TagColorList.length]
  return template(
    `<div class="{{wrapperClass}}">
            <div class="{{class}}" style="{{style}}">{{value}}</div>
         </div>`,
    {
      wrapperClass: 'flex',
      class: 'p-2 rounded text-[#1f1616] leading-none',
      style: `background-color: ${tagColor}`,
      value
    }
  )
}

// 列标题格式
const colTitleFormatter: Formatter = (cell, formatterParams) => {
  const { column } = formatterParams as { column: ColumnConfig }
  return template(
    `
    <div class="{{wrapperClass}}">
      <i class="{{iconClass}}" ></i>
      <span class="{{titleClass}}">{{title}}</span>
    </div>
  `,
    {
      wrapperClass: 'flex gap-x-2 items-center',
      iconClass: `${typeIcon[column.type]} shrink-0`,
      titleClass: 'truncate',
      title: cell.getColumn().getDefinition().title
    }
  )
}
// 自定义枚举列的排序规则，按 enums 正序或逆序排序
const enumColSorter: ColumnDefinition['sorter'] = (
  a,
  b,
  _aRow,
  _bRow,
  _column,
  dir,
  sorterParams
) => {
  // 空值排在最前
  if (!a) return -1
  if (!b) return 1

  const { column } = sorterParams as { column: ColumnConfig }
  // 所有排序配置
  const sorters = tabulator.value?.getSorters() ?? []
  // 当前排序字段
  const field = _column.getField()
  // 当前排序字段再排序配置中的位置
  const sorterIndex = sorters.findIndex(s => s.field === field)
  // 当前排序字段不是第一个排序配置
  if (sorterIndex > 0) {
    // 行数据
    const aData = _aRow.getData()
    const bData = _bRow.getData()
    // 遍历所有在当前排序配置之前的配置，如果前面的排序字段的值不同，则当前字段不进行排序
    // 即排序配置的索引越小，排序权重越高，对于a,b两行，排序权重高的字段的值不同，表示已经按高权重字段排好序，本次排序跳过
    for (let i = 0; i < sorterIndex; i++) {
      const compareKey = sorters[i].field
      if (aData[compareKey] !== bData[compareKey]) return 0
    }
  }
  const enums = column.enums ?? []
  const aIndex = enums.findIndex(item => item === a)
  const bIndex = enums.findIndex(item => item === b)
  if (aIndex === bIndex) return 0
  return dir === 'asc' ? (aIndex > bIndex ? 1 : -1) : bIndex > aIndex ? -1 : 1
}

const defineTabulatorColumns = (): ColumnDefinition[] => {
  return props.columns.map(column => {
    if (column.type === 'enum') {
      colEnumMap.value[column.field] = column.enums ?? []
    }
    const definition = {
      ...omit(column, ['type', 'enums']),
      width: column.field.length * 16 + 40,
      headerSort: false,
      resizable: 'header',
      titleFormatter: colTitleFormatter,
      titleFormatterParams: () => ({ column })
    } as ColumnDefinition
    if (column.type === 'enum') {
      definition.formatter = tagCellFormatter
      definition.formatterParams = () => ({ column })
      definition.sorter = enumColSorter
      definition.sorterParams = () => ({ column })
    }
    return definition
  })
}

const defineRowHeader = (): OptionsGeneral['rowHeader'] => {
  return {
    formatter: 'rownum',
    titleFormatter: 'rowSelection',
    resizable: false,
    hozAlign: 'center',
    frozen: true,
    headerSort: false,
    width: 60,
    cellClick: (_e, cell) => {
      cell.getRow().toggleSelect()
    }
  }
}

/**#region popover state */

// 字段管理按钮
const fieldButtonRef = ref()
// 字段管理组件实例
const fieldManagementRef = ref()
// 字段选择器实例
const fieldPickerRef = ref()
// 筛选组件实例
const filterConfigRef = ref()
// 比较操作符选择器实例
const symbolPickerRef = ref()
// 分组组件实例
const groupingConfigRef = ref()
// 排序组件实例
const sortingConfigRef = ref()
// 筛选按钮
const filterButtonRef = ref()
// 分组按钮
const groupingConfigButtonRef = ref()
// 排序按钮
const sortingConfigButtonRef = ref()

/**#endregion */

/**#region 筛选 */

// 筛选条件配置
const filterConfig: Ref<FilterConfig> = ref({
  logicSymbol: null,
  conditions: []
})
// 是否有筛选条件
const hasFilter = computed(() => filterConfig.value.conditions.length > 0)

// 打开筛选面板
const onSettingUpFilter = async (e: MouseEvent) => {
  if (hasFilter.value) {
    filterConfigRef.value?.toggleVisible(true)
    return
  }
  const picker = fieldPickerRef.value
  picker.setVirtualRef(e.target)
  picker.setSelectableColumns(props.columns)
  picker.toggleVisible(true)
  const [cancel, field] = await tryit<never[], Promise<ColumnConfig>>(
    picker.pick
  )()
  if (cancel) return
  filterConfigRef.value.handlePickFilterField(field)
}

// TODO: 筛选条件固定到顶部工具栏
// 是否可以将筛选项固定到顶部工具栏（）最多固定三个


const filterHandler = debounce((config: FilterConfig) => {
  // 过滤有效的筛选条件
  const effectiveFilterConditions = config.conditions.filter(c => {
    if (['isEmpty', 'isNotEmpty'].includes(c.compareSymbol)) return true
    return c.value && c.value.length > 0 && c.value.every(v => !!v)
  })
  const data = select(
    props.data,
    d => d,
    item => {
      // 没有有效的过滤条件，返回全部数据
      if (effectiveFilterConditions.length === 0) return true
      const compareResult = effectiveFilterConditions.map(condition => {
        // 数据值
        const compareValue = item[condition.field]
        // 比较方法
        const compareMethod = CompareSymbol[condition.compareSymbol]
        // 先进行 为空、非空过滤
        if (['isEmpty', 'isNotEmpty'].includes(condition.compareSymbol)) {
          return compareMethod(compareValue)
        }
        // 文本、数字直接调用比较方法将行数据中的值与条件值进行比较
        if (['text', 'number'].includes(condition.$type)) {
          let _conditionValue = condition.value[0]
          let _compareValue = compareValue
          if (condition.$type === 'number') {
            _conditionValue = +(_conditionValue ?? '')
            _compareValue = +_compareValue
          }
          return compareMethod(_compareValue, _conditionValue)
        }
        // 处理日期的过滤
        if (condition.$type === 'date') {
          const dateConditionType = condition.value[0] as
            | keyof typeof DateConditionValue
            | 'specificDate'
          // 过滤条件为指定日期，直接调用比较函数
          if (dateConditionType === 'specificDate')
            return compareMethod(compareValue, condition.value[1])
          // 非指定日期，获取比较条件值
          const conditionValue = DateConditionValue[dateConditionType]()
          // 如果条件值是数组，则条件值一定是 [开始日期，结束日期]
          // 返回数据值是不是在开始日期和结束日期时间范围内，即 开始日期 <= 数据值 <= 结束日期
          if (Array.isArray(conditionValue)) {
            const [start, end] = conditionValue
            return (
              CompareSymbol.greaterThanOrEqual(
                dayjs(compareValue).valueOf(),
                start
              ) &&
              CompareSymbol.lessThanOrEqual(dayjs(compareValue).valueOf(), end)
            )
          }
          // 条件值不是数组，调用比较函数返回数据值和条件值的比较结果
          return compareMethod(compareValue, conditionValue)
        }

        // 枚举值比较
        if (!compareValue) return false
        return compareMethod(condition.value.join(','), compareValue)
      })
      // 如果有多个比较结果，使用 && 或 || 合并
      if (compareResult.length > 1)
        return LogicSymbol[config.logicSymbol ?? 'AND'](compareResult)
      return compareResult[0]
    }
  )
  tabulator.value?.setData(data)
}, 200)

watch(
  () => filterConfig.value,
  val => {
    filterHandler(val)
    tableDataProcessConfig.value.filter = deepClone(filterConfig.value)

  },
  { deep: true }
)

/**#endregion */

/**#region 分组 */

const groupingConfig: Ref<GroupingConfig[]> = ref([])

const hasGrouping = computed(() => groupingConfig.value.length > 0)

// 打开分组面板
const onSettingUpGrouping = async (e: MouseEvent) => {
  if (hasGrouping.value) {
    groupingConfigRef.value?.toggleVisible(true)
    return
  }
  const picker = fieldPickerRef.value
  picker.setVirtualRef(e.target)
  picker.setSelectableColumns(props.columns)
  picker.toggleVisible(true)
  const [cancel, field] = await tryit<never[], Promise<ColumnConfig>>(
    picker.pick
  )()
  if (cancel) return
  groupingConfigRef.value.handlePickGroupingField(field)
}

const setTableGroupingAndSorting = () => {
  tabulator.value!.setGroupBy(
    groupingConfig.value.map(conf => conf.refCol?.field ?? '')
  )
  tabulator.value?.setSort([
    ...groupingConfig.value.map(conf => ({
      column: conf.refCol?.field,
      dir: conf.sort
    })),
    ...sortingConfig.value.map(conf => ({
      column: conf.refCol?.field,
      dir: conf.sort
    }))
  ])
}

const onUpdateGroupBy = () => {
  sortingConfig.value = sortingConfig.value.filter(
    conf =>
      !groupingConfig.value.map(c => c.refCol.field).includes(conf.refCol.field)
  )
  setTableGroupingAndSorting()
}

/**#endregion */

/**#region 排序 */

const sortingConfig: Ref<SortingConfig[]> = ref([])
const hasSorting = computed(() => sortingConfig.value.length > 0)

const selectableSortingColumns = computed(() =>
  props.columns.filter(
    col =>
      !(groupingConfig.value.map(conf => conf.refCol?.field) ?? []).includes(col.field)
  )
)
const onSettingUpSorting = async (e: MouseEvent) => {
  if (hasSorting.value) {
    sortingConfigRef.value?.toggleVisible(true)
    return
  }
  const picker = fieldPickerRef.value
  picker.setVirtualRef(e.target)
  picker.setSelectableColumns(selectableSortingColumns.value)
  picker.toggleVisible(true)
  const [cancel, field] = await tryit<never[], Promise<ColumnConfig>>(
    picker.pick
  )()
  if (cancel) return
  sortingConfigRef.value.handlePickSortingField(field)
}

const setTableSorting = () => {
  const sortList = [
    ...groupingConfig.value.map(conf => ({
      column: conf.refCol.field,
      dir: conf.sort
    })),
    ...sortingConfig.value.map(conf => ({
      column: conf.refCol.field,
      dir: conf.sort
    }))
  ]
  tabulator.value?.setSort(sortList)
}

/**#endregion */

const onUpdateColumnDefinition = () => {
  tableLayout.value = tabulator.value?.getColumnLayout().filter(col => !!col.field) ?? []
}

/**
 * 从外部配置读取表格数据处理的配置（筛选、分组、排序）
 */
const restoreDataProcessConfig = () => {
  const {
    sorter = [],
    grouping = [],
    filter = { logicSymbol: null, conditions: [] }
  } = tableDataProcessConfig.value ?? {
    sorter: [],
    grouping: [],
    filter: { logicSymbol: null, conditions: [] }
  }
  sortingConfig.value = sorter.map(sort => ({
    refCol: props.columns.find(col => col.field === sort.field)!,
    sort: sort.dir
  }))
  groupingConfig.value = grouping.map(conf => ({
    refCol: props.columns.find(col => col.field === conf.field)!,
    sort: conf.dir
  }))
  filterConfig.value = deepClone(filter)
}

const onTableBuilt = () => {
  tabulatorColumns.value = tabulator.value!.getColumnDefinitions().filter(col => Reflect.has(col, 'field')).map(col => ({
    ...col,
    visible: true
  }))
  emit('init')
}

const setLayout = () => {
  tabulatorColumns.value = tabulatorColumns.value.reduce((acc: ColumnDefinition[], curr: ColumnDefinition) => {
    const newLayout = tableLayout.value.find(value => value.field === curr.field)
    if (newLayout) {
      acc.push({
        ...curr,
        ...newLayout
      })
    } else {
      acc.push(curr)
    }
    return acc
  }, [])
  tabulator.value?.setColumns(tabulatorColumns.value)
  onUpdateColumnDefinition()
}

const setConfig = () => {
  restoreDataProcessConfig()
  setTableGroupingAndSorting()
  tableLayout.value = tabulator.value?.getColumnLayout().filter(col => !!col.field) ?? []
}


watchEffect(() => {
  if (props.columns.length > 0) {
    tabulator.value.setColumns(defineTabulatorColumns())
    nextTick().then(() => {
      tabulatorColumns.value = tabulator.value!.getColumnDefinitions().filter(col => Reflect.has(col, 'field')).map(col => ({
        ...col,
        visible: true
      }))
      onUpdateColumnDefinition()
    })
  }
  if (props.data.length > 0) {
    tabulator.value.setData(props.data)
    console.log({
      data: tabulator.value?.getData(),
      columns: tabulator.value?.getColumnDefinitions()
    })
  }
})


defineExpose({
  setLayout,
  setConfig,
})

onMounted(async () => {
  const columns = defineTabulatorColumns()
  const rowHeader = defineRowHeader()
  tabulatorRowHeader.value = rowHeader

  if (tabulator.value) {
    tabulator.value!.destroy()
    tabulator.value = null
  }

  tabulator.value = new Tabulator('#tabulator', {
    height: withDefaultUnit(tabulatorWrapperRef.value.clientHeight - 12),
    renderHorizontal: 'virtual', // 使用虚拟 dom 进行水平渲染
    // 禁止自动调整列宽
    autoResize: false,
    // 显示列宽调整引导线
    resizableColumnGuide: true,
    // 允许列拖拽
    movableColumns: true,
    // 允许行拖拽
    movableRows: true,
    data: props.data,
    rowHeader,
    columns
  })
  await nextTick()

  tabulator.value.on('tableBuilt', onTableBuilt)
})

const handleBeforeUnload = () => {
  emit('before-destroy')
}

onBeforeMount(() => {
  window.addEventListener('beforeunload', handleBeforeUnload)
})

onBeforeUnmount(() => {
  window.removeEventListener('beforeunload', handleBeforeUnload)
  emit('before-destroy')
})
onUnmounted(() => {
  if (tabulator.value) {
    tabulator.value!.destroy()
    tabulator.value = null
  }
})
</script>
